import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import Stats from 'three/examples/jsm/libs/stats.module.js'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
export class Base {
    renderer
    stats
    camera
    scene
    controls
    clock = new THREE.Clock()
    constructor(elelemtId:string, showSky = true) {
        this.renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } )
        this.stats = Stats()
        this.camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 )
        this.scene = new THREE.Scene() 
        const container = document.getElementById(elelemtId)
        const { renderer, camera, scene, stats } = this
        renderer.setPixelRatio( window.devicePixelRatio )
        renderer.setSize( window.innerWidth, window.innerHeight )
        renderer.outputEncoding = THREE.sRGBEncoding
        renderer.shadowMap.enabled = true;
        container?.appendChild( renderer.domElement )
        showSky && container?.appendChild( stats.dom )
        // scene.background = new THREE.Color( 0xa0a0a0 )
        this.initLight()
        this.controls = new OrbitControls(camera, renderer.domElement)
        this.controls.maxPolarAngle = THREE.MathUtils.degToRad(80)
        // this.controls.maxDistance = 200
        this.controls.minDistance = 10
        
        showSky && this.initSky()
        setTimeout(async () => {
            await this.initObject()
            this.animate()
        })
        
    }
    create3dText( text:string, opt?: {
		fontFamily?: string, 
		fontSize?: number, 
		fontWeight?: string, 
		lineHeight?: number, 
		color?: string, 
		borderWidth?: number, 
		borderRadius?: number, 
		height?: number,
        bl?:number,
		borderColor?: string, 
		backgroundColor?: string
	} ) { 
		const option = { 
			fontFamily: 'Microsoft Yahei', 
			fontSize: 40, 
			fontWeight: 'normal', 
			lineHeight: 1.4, 
		 	color: '#ffffff', 
			borderWidth: 4, 
			borderRadius: 6, 
            bl: 1,
			height: 50,
			borderColor: 'transparent', 
			backgroundColor: 'transparent'
		}
        
		Object.assign(option, opt)
        option.fontSize *= option.bl
        option.height *= option.bl
		const canvas = document.createElement('canvas') as any
		const textWidth = text.length * option.fontSize - text.replace(/[\u4e00-\u9fa5]/g, '').length * option.fontSize / 2
		canvas.width = 356 * option.bl
		canvas.height = option.height
		const context = canvas.getContext('2d') as any
		context.font = option.fontWeight + ' ' + option.fontSize + 'px ' + option.fontFamily; 
		
		
		const texture = new THREE.Texture(canvas)
		texture.needsUpdate = true
		const materialObj = new THREE.SpriteMaterial({ map: texture })
		const spriteObj = new THREE.Sprite( materialObj )
		const bl = 96
		spriteObj.scale.set(bl, Math.ceil(bl / (canvas.width / canvas.height)), bl)
		context.fillStyle = option.backgroundColor; 
		context.strokeStyle = option.borderColor;
		context.fillStyle = '#ffffff'; 
		context.strokeStyle = option.color
		context.fillText(text, option.borderWidth + (canvas.width - textWidth) / 2, option.fontSize + option.borderWidth);
        context.strokeText(text, option.borderWidth + (canvas.width - textWidth) / 2, option.fontSize + option.borderWidth); 
		
		return spriteObj; 
	}
    async initSky() {
        const path = './images/skybox1/'
        const urls = [
            path + '6.png',
            path + '3.png',
            path + '2.png',
            path + '1.png',
            path + '5.png',
            path + '4.png'
        ]
        const textCube = new THREE.CubeTextureLoader().load(urls)
        // textCube.encoding = THREE.sRGBEncoding
        this.scene.background = textCube
        
    }
    createGltf(path:string) {
        return new Promise<THREE.Group>((resolve, reject) => {
            const loader = new GLTFLoader();
            loader.load( `models/${path}/scene.gltf`, function ( gltf ) {
                resolve(gltf.scene)
            } );
        })
    }
    createFbx(path:string) {
        return new Promise<THREE.Group>((resolve) => {
            const loader = new FBXLoader();
            loader.load( 'models/'+path+'.fbx', function ( object ) {

                object.traverse( function ( child:any ) {

                    if ( child.isMesh ) {

                        // child.castShadow = true;
                        // child.receiveShadow = true;
                        // child.frustumCulled = false;

                    }

                } );
                resolve( object );

            } );
        })
    }
    loadGL(path: string, name?:string) {
		const loader = new GLTFLoader();
		return new Promise<{
            scene: THREE.Group,
            animations: THREE.AnimationClip[]

        }>((resolve, reject) => {
			loader.load(`/models/${path}/scene.gltf`, ( gltf ) => { 
                
                gltf.scene.traverse( function ( child:any ) {
                    if ( child.isMesh ) {
                        // child.frustumCulled = false;
                        //模型阴影
                        child.castShadow = true;
                        //模型自发光
                        // child.material.emissive =  child.material.color;
                        // child.material.emissiveMap = child.material.map ;
                    }
                })
				
				resolve(gltf)
	
			}, ( xhr ) => {
	
			}, ( error ) => {
	
				console.log( 'An error happened' );
	
			}
		)
		})
	}
    initLight() {
        const { scene } = this
        const light = new THREE.AmbientLight( 0xffffff, 0.5 ) // soft white light
        scene.add( light )

        const dirLight = new THREE.DirectionalLight( 0xffffff, 0.5)
        dirLight.position.set( -3, 10, 0 );
        // dirLight.castShadow = true;
        // dirLight.shadow.camera.top = 4;
        // dirLight.shadow.camera.bottom = -4;
        // dirLight.shadow.camera.left = -4;
        // dirLight.shadow.camera.right = 4;
        // dirLight.shadow.camera.near = 0.1;
        // dirLight.shadow.camera.far = 40;
        this.scene.add( dirLight )

        // const directionalLight2 = new THREE.DirectionalLight( 0xffffffff, 0.5 )
        // directionalLight2.position.set(1, 1, -1)
        // this.scene.add( directionalLight2 )
        
        
        window.addEventListener( 'resize', () => this.onWindowResize() )
    }
    async initObject() {
        
    }
    onWindowResize() {
        const { camera, renderer } = this
        const aspect = window.innerWidth / window.innerHeight
        camera.aspect = aspect
        camera.updateProjectionMatrix()
        renderer.setSize( window.innerWidth, window.innerHeight )
    }
    onRender() {

    }
    animate() {
        const {renderer, scene, camera, stats, controls } = this
        stats?.update()
        controls.update()
        
        renderer.render( scene, camera )
        this.onRender()
        requestAnimationFrame(this.animate.bind(this) )
    }
}